package top.chenfu.blog.config.security;

import com.alibaba.fastjson.JSON;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.*;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import top.chenfu.blog.beans.ResponseData;
import top.chenfu.blog.service.UserService;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * @Auther chenfu
 * @2019/10/30 15:04
 * @Desc
 */
@SpringBootConfiguration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserService userDetailsService;

    public static void main(String[] args) {
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        String encode = encoder.encode("123");
        System.out.println(encode);
    }

    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 认证
     *
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
    }

    /**
     * 授权
     *
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.authorizeRequests()
                .antMatchers("/article/all").permitAll()
                .antMatchers("/article/dataStatistics").authenticated()
                .antMatchers(HttpMethod.GET, "/article/*").permitAll()
                .antMatchers("/admin/category/all").authenticated()
//                /admin/**的URL都需要有超级管理员角色，
//                如果使用.hasAuthority()方法来配置，需要在参数中加上ROLE_,
//                如下.hasAuthority("ROLE_超级管理员")
                .antMatchers("/admin/**", "/reg").hasRole("超级管理员")
//                其他的路径都是登录后即可访问
                .anyRequest().authenticated()
                .and().formLogin()
                .loginProcessingUrl("/login")
                .usernameParameter("username")
                .passwordParameter("password")
                .loginPage("/login_page")
                .successHandler(new AuthenticationSuccessHandler() {
                    @Override
                    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
                        response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
                        ResponseData success = ResponseData.defaultSuccess();
                        success.setMsg("登录成功");
                        PrintWriter out = response.getWriter();
                        out.write(JSON.toJSONString(success));
                        out.flush();
                        out.close();
                    }
                }).failureHandler(new AuthenticationFailureHandler() {
            @Override
            public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
                response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
                response.setStatus(401);
                ResponseData fail = ResponseData.defaultFail();
                if (e instanceof BadCredentialsException || e instanceof UsernameNotFoundException) {
                    fail.setMsg("账户名或者密码输入错误!");
                } else if (e instanceof LockedException) {
                    fail.setMsg("账户被锁定，请联系管理员!");
                } else if (e instanceof CredentialsExpiredException) {
                    fail.setMsg("密码过期，请联系管理员!");
                } else if (e instanceof AccountExpiredException) {
                    fail.setMsg("账户过期，请联系管理员!");
                } else if (e instanceof DisabledException) {
                    fail.setMsg("账户被禁用，请联系管理员!");
                } else {
                    fail.setMsg("登录失败!");
                }
                PrintWriter out = response.getWriter();
                out.write(JSON.toJSONString(fail));
                out.flush();
                out.close();
            }
        }).permitAll()
                .and().logout().logoutUrl("/logout")
                .logoutSuccessHandler(new LogoutSuccessHandler() {
                    @Override
                    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
                        response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
                        ResponseData success = ResponseData.defaultSuccess();
                        success.setMsg("注销成功");
                        PrintWriter out = response.getWriter();
                        out.write(JSON.toJSONString(success));
                        out.flush();
                        out.close();
                    }
                }).permitAll()
                .and().csrf().disable().exceptionHandling().accessDeniedHandler(new AccessDeniedHandler() {
            @Override
            public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException {
                response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
                response.setStatus(HttpServletResponse.SC_FORBIDDEN);
                ResponseData fail = ResponseData.defaultFail();
                fail.setMsg("权限不足，请联系管理员");
                PrintWriter out = response.getWriter();
                out.write(JSON.toJSONString(fail));
                out.flush();
                out.close();
            }
        });
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/blogimg/**", "/index.html", "/static/**");
    }

}
